﻿using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Runtime.Serialization.Json;
using System.Security.Cryptography.X509Certificates;
using System.Web;
using System.Windows;
using System.Windows.Controls;
using Newtonsoft.Json;

namespace Client {
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window {
       
        WebClient clientW;
       
        DateTime date = DateTime.Now;
        string fileName = DateTime.Now.ToFileTime() + ".txt";
        StreamWriter log;
        string serverName = "Bank";

        public MainWindow() {
            InitializeComponent();
            ServicePointManager.ServerCertificateValidationCallback += ValidateServerCertificate;
        }

        #region SSL

        // The following method is invoked by the RemoteCertificateValidationDelegate.
        public static bool ValidateServerCertificate(
              object sender,
              X509Certificate certificate,
              X509Chain chain,
              SslPolicyErrors sslPolicyErrors)
        {
            //if (sslPolicyErrors == SslPolicyErrors.None)
            //    return true;
            if(certificate.Issuer.Contains("Bank"))
                return true;

            //Console.WriteLine("Certificate error: {0}", sslPolicyErrors);

            //// Do not allow this client to communicate with unauthenticated servers.
            //return false;
            return false;
        }

        #endregion

        /// <summary>
        /// Callback for connect button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void connect_Click(object sender, RoutedEventArgs e) {
            try {

                string ip = tbIP.Text;
                int port = int.Parse(tbPort.Text);

                // Create socket
                clientW = new WebClient();


                // Update UI
                tbIP.Visibility = Visibility.Hidden;
                tblockIP.Visibility = Visibility.Hidden;
                tblockPort.Visibility = Visibility.Hidden;
                tbPort.Visibility = Visibility.Hidden;
                btn_connect.Visibility = Visibility.Hidden;
                cb_action.Visibility = Visibility.Visible;
                textBlockAction.Visibility = Visibility.Visible;
                btn_send.Visibility = Visibility.Visible;
                btn_close.Visibility = Visibility.Visible;

                using (StreamWriter log = new StreamWriter(fileName,true)) {
                    log.WriteLine("{0} Client connected to {1}:{2}", date.ToLocalTime(),ip,port);
                }
               

            } catch (ArgumentNullException ex) {
                Console.WriteLine("{0} ArgumentNullException: {1}", date.ToLocalTime(), ex);
                using (StreamWriter log = new StreamWriter(fileName, true)) {
                    log.WriteLine("{0} ArgumentNullException: {1}", date.ToLocalTime(), ex);
                }
            } catch (FormatException ex) {
                MessageBoxResult result = MessageBox.Show("Error en el campo Puerto", "Error");
                Console.WriteLine("{0} FormatException: {1}", date.ToLocalTime(), ex);
                using (StreamWriter log = new StreamWriter(fileName, true)) {
                    log.WriteLine("{0} FormatException: {1}", date.ToLocalTime(), ex);
                }
            } catch (SocketException ex) {
                MessageBoxResult result = MessageBox.Show("Error en la conexión TCP", "Error");
                Console.WriteLine("{0} SocketException: {1}", date.ToLocalTime(), ex);
                using (StreamWriter log = new StreamWriter(fileName, true)) {
                    log.WriteLine("{0} SocketException: {1}", date.ToLocalTime(), ex);
                }
            }
        }

        /// <summary>
        /// Callback for close button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_close_Click(object sender, RoutedEventArgs e) {
            try {
                // Close stream and socket connection
               

                MessageBoxResult result = MessageBox.Show("Conexión terminada correctamente", "Cerrar conexión");
                Console.WriteLine("{0} Terminó la conexión!", date.ToLocalTime());

                using (StreamWriter log = new StreamWriter(fileName, true)) {
                    log.WriteLine("{0} Terminó la conexión!", date.ToLocalTime());
                }                

            } catch (Exception ex) {
                MessageBoxResult result = MessageBox.Show("Error al intentar cerrar la conexión", "Error");
                Console.WriteLine("{0} Exception: {0}", date.ToLocalTime(), ex);
                using (StreamWriter log = new StreamWriter(fileName, true)) {
                    log.WriteLine("{0} Exception: {0}", date.ToLocalTime(), ex);
                }
            }

            // Update UI
            tbIP.Visibility = Visibility.Visible;
            tblockIP.Visibility = Visibility.Visible;
            tbPort.Visibility = Visibility.Visible;
            tblockPort.Visibility = Visibility.Visible;
            btn_connect.Visibility = Visibility.Visible;

            cb_action.Visibility = Visibility.Hidden;
            textBlockAction.Visibility = Visibility.Hidden;
            btn_send.Visibility = Visibility.Hidden;
            btn_close.Visibility = Visibility.Hidden;
            textBlockAccount.Visibility = Visibility.Hidden;
            textBlockQuantity.Visibility = Visibility.Hidden;
            tbAccount.Visibility = Visibility.Hidden;
            tbQuantity.Visibility = Visibility.Hidden;
            cb_action.SelectedIndex = 0;
        }

        /// <summary>
        /// Callback for send button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_send_Click(object sender, RoutedEventArgs e) {
            try {
                // Create the request message for the server
                RequestMessage requestMessage = new RequestMessage();

                // Add selected parameters
                requestMessage.action = (cb_action.SelectedIndex + 1).ToString();
                requestMessage.account = tbAccount.Text;
                requestMessage.quantity = tbQuantity.Text;

                

                //SSL

                //SslStream sslStream = new SslStream(
                //    stream,
                //    false,
                //    new RemoteCertificateValidationCallback(ValidateServerCertificate),
                //    null
                //);

                //// The server name must match the name on the server certificate.
                //try
                //{
                //    sslStream.AuthenticateAsClient(serverName);
                //}
                //catch (AuthenticationException ex)
                //{
                //    Console.WriteLine("Exception: {0}", ex.Message);
                //    if (ex.InnerException != null)
                //    {
                //        Console.WriteLine("Inner exception: {0}", ex.InnerException.Message);
                //    }
                //    Console.WriteLine("Authentication failed - closing the connection.");
                //    client.Close();
                //    return;
                //}

                //SSL

                // Create JSON Serializer for request message.
                DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(RequestMessage));
                string json;

                using (MemoryStream ms = new MemoryStream()) 
                {
                    ser.WriteObject(ms, requestMessage);
                    ms.Seek(0, SeekOrigin.Begin);
                    using (StreamReader sr = new StreamReader(ms))
                    {
                        json = sr.ReadToEnd();
                    }
                }

                string response;
                
                string request = String.Format("https://{0}:{1}/?data={2}", tbIP.Text, tbPort.Text, HttpUtility.UrlEncode(json));
                response = clientW.DownloadString(request);

                    
                //ser.WriteObject(sslStream, requestMessage);
               
                // Create response message from server.
                ResponseMessage responseMessage = new ResponseMessage();

              
                // Deserialize JSON object from server.
                responseMessage = JsonConvert.DeserializeObject<ResponseMessage>(response);

                // Handle response
                if (String.Equals(responseMessage.error, "False", StringComparison.OrdinalIgnoreCase)) {
                    MessageBoxResult result;
                    String message;
                    switch (cb_action.SelectedIndex) {
                        case 0:
                            // Handle Create New Account
                            message = String.Format("{0} Nueva cuenta creada! Cuenta: {1}", date.ToLocalTime(), responseMessage.account);
                            Console.WriteLine(message);
                            using (StreamWriter log = new StreamWriter(fileName, true)) {
                                log.WriteLine(message);
                            }
                            result = MessageBox.Show(message, "Creación de la cuenta");
                            break;
                        case 1:
                            // Handle balance check
                            message = String.Format("{0} El saldo de la cuenta {1} es de ${2}", date.ToLocalTime(), responseMessage.account, responseMessage.quantity);
                            Console.WriteLine(message);
                            using (StreamWriter log = new StreamWriter(fileName, true)) {
                                log.WriteLine(message);
                            }
                            result = MessageBox.Show(message, "Consulta de Saldo");
                            break;
                        case 2:
                            // Handle Account Deposit
                            message = String.Format("{0} Se depositaron ${1} correctamente", date.ToLocalTime(), requestMessage.quantity);
                            //String m2 = String.Format("{0} El saldo de la cuenta {1} ahora es de ${2}", date.ToLocalTime(), responseMessage.account, responseMessage.quantity);
                            Console.WriteLine(message);
                            //Console.WriteLine(m2);
                            using (StreamWriter log = new StreamWriter(fileName, true)) {
                                log.WriteLine(message);
                            }
                            result = MessageBox.Show(String.Format("{0} ", message), "Depósito de dinero");
                            break;
                        case 3:
                            // Handle Account Withdrawal
                            message = String.Format("{0} Se retiraron ${1} correctamente", date.ToLocalTime(), requestMessage.quantity);
                            //String m3 = String.Format("{0} El saldo de la cuenta {1} ahora es de ${2}", date.ToLocalTime(), responseMessage.account, responseMessage.quantity);
                            Console.WriteLine(message);
                            using (StreamWriter log = new StreamWriter(fileName, true)) {
                                log.WriteLine(message);
                            }
                            //Console.WriteLine(m3);
                            result = MessageBox.Show(String.Format("{0}", message), "Retiro de dinero");
                            break;
                        case 4:
                            // Handle Server Ping
                            message = String.Format("{0} Mensaje:{1}", date.ToLocalTime(), responseMessage.account);
                            Console.WriteLine(message);
                            using (StreamWriter log = new StreamWriter(fileName, true)) {
                                log.WriteLine(message);
                            }
                            result = MessageBox.Show(message);
                            break;
                    }
                } else {
                    // Handle error
                    String m = String.Format("{0} Ocurrió un error al efectuar la operación {1}\n{2}", date.ToLocalTime(), cb_action.Text, responseMessage.account);
                    Console.WriteLine(m);
                    using (StreamWriter log = new StreamWriter(fileName, true)) {
                        log.WriteLine(m);
                    }
                    MessageBoxResult result = MessageBox.Show(m);
                }

            } catch (SocketException ex) {
                MessageBoxResult result = MessageBox.Show("Error en el socket al enviar la petición", "Error");
                Console.WriteLine("{0} SocketException: {1}", date.ToLocalTime(), ex);
                using (StreamWriter log = new StreamWriter(fileName, true)) {
                    log.WriteLine("{0} SocketException: {1}", date.ToLocalTime(), ex);
                }
            } catch (Exception ex) {
                MessageBoxResult result = MessageBox.Show("Error al enviar la petición", "Error");
                Console.WriteLine("{0} Exception: {1}", date.ToLocalTime(), ex.Message);
                using (StreamWriter log = new StreamWriter(fileName, true)) {
                    log.WriteLine("{0} Exception: {1}", date.ToLocalTime(), ex.Message);
                }
            } finally {
                
            }
        }

        /// <summary>
        /// Handle UI changes for requests
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cb_action_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            switch (((ComboBox)sender).SelectedIndex) {
                case 0:
                    // New Account
                    textBlockAccount.Visibility = Visibility.Hidden;
                    textBlockQuantity.Visibility = Visibility.Hidden;
                    tbAccount.Visibility = Visibility.Hidden;
                    tbQuantity.Visibility = Visibility.Hidden;
                    break;
                case 1:
                    // Check balance
                    textBlockAccount.Visibility = Visibility.Visible;
                    textBlockQuantity.Visibility = Visibility.Hidden;
                    tbAccount.Visibility = Visibility.Visible;
                    tbQuantity.Visibility = Visibility.Hidden;
                    break;
                case 2:
                case 3:
                    // Deposit/Withdraw
                    textBlockAccount.Visibility = Visibility.Visible;
                    textBlockQuantity.Visibility = Visibility.Visible;
                    tbAccount.Visibility = Visibility.Visible;
                    tbQuantity.Visibility = Visibility.Visible;
                    break;
                case 4:
                    // Server Ping
                    textBlockAccount.Visibility = Visibility.Hidden;
                    textBlockQuantity.Visibility = Visibility.Hidden;
                    tbAccount.Visibility = Visibility.Hidden;
                    tbQuantity.Visibility = Visibility.Hidden;
                    break;
            }
        }
    }
}
